feat(webauthn): largeBlob write and delete#261
Merged
Conversation
Add new_set_first and new_set_continuation per CTAP 2.2 §6.10.2: length is present only when offset is zero, omitted otherwise. Both take Option<(auth_param, protocol)> so unprotected authenticators can issue the command without pinUvAuthParam.
Implements WebAuthn L3 §10.1.5 write and a libwebauthn-side delete on top of CTAP 2.2 §6.10.6 update-or-erase. The platform fetches the existing large-blob array, drops any entry that decrypts under the credential's largeBlobKey, optionally appends a fresh encrypted entry, and re-uploads the canonical CBOR array with a fresh SHA-256 trailer. Foreign entries (different key, malformed, unknown fields) are preserved byte-for-byte per the §6.10.2 platform contract. Upload is chunked per maxFragmentLength with pinUvAuthParam computed as authenticate(token, 32*0xff || 0x0c 0x00 || u32_le(offset) || SHA-256(set)) per §6.10.2. The Ctap2GetAssertionRequest now ORs LARGE_BLOB_WRITE into its permissions when write or delete is requested so user_verification negotiates a token covering the lbw permission. To keep PIN-protected devices working under UV=Discouraged, the Ctap2UserVerifiableRequest trait gains needs_pin_uv_auth_token, which suppresses the OnlyForSharedSecret downgrade in user_verification. Unprotected authenticators continue to accept the write without auth params per spec line 137. Delete with no matching entry returns LargeBlobError::NoMatch (written=false), matching the strict §6.10.6 'Return an error' branch at line 303.
Exercises the WebAuthn write/delete extensions against the virt authenticator end-to-end: - write then read returns the planted bytes - second write replaces the first entry - delete after write removes the entry; subsequent read returns no blob - delete with no prior entry reports written=false
Preserve foreign largeBlobArray entries by their exact bytes on read-modify-write rather than re-serialising them, which also drops a redundant re-encode of the new entry. Treat a valid-trailer but unparseable array as a fail-safe error rather than clobbering it. Satisfy the production clippy::indexing_slicing denies in the write path. Refuse the U2F downgrade for largeBlob write and delete. Adds tests for nonce uniqueness across writes, the pinUvAuthParam protocol-1 path, the JSON write input, the PIN-protected write token ceremony under UV=Discouraged, foreign-blob survival across write and delete, the single-allowCredential guard, and shrink-on-replace.
c18ac78 to
6799ed6
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds the WebAuthn L3 largeBlob write extension and a libwebauthn-side delete operation, on top of CTAP 2.2 authenticatorLargeBlobs.
After an assertion yields the per-credential key, the platform fetches the on-authenticator blob array, replaces or erases the entry belonging to this credential, and re-uploads the array in chunks. Other credentials' blobs and any unknown fields are preserved byte for byte. The lbw permission is negotiated into the get-assertion token so a single user verification covers the write. Unprotected authenticators are written without auth params.
Write and delete failures are non-fatal and surface as written=false rather than failing the assertion.
Stacked on #206 (largeBlob read). Merge that first. Supersedes #224.